package com.minesweeper.tools;

import com.minesweeper.data.GameData;
import com.minesweeper.data.ImagesData;
import com.minesweeper.ui.Block;
import com.minesweeper.ui.GameInterface;

import javax.swing.*;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.*;
import java.nio.charset.StandardCharsets;


/**
 * @author skniy
 */
public class MineListener implements MouseListener {

    /**
     * 格子信息
     */
    private Block[][] blocks;
    /**
     * 游戏界面
     */
    private GameInterface gameInterface;
    /**
     * 捷径，是否左右键同时按下
     */
    private boolean shortcut;
    /**
     * 游戏失败音乐
     */
    private GameMusic gameOverMusic;
    /**
     * 游戏胜利音乐
     */
    private GameMusic gameWinMusic;
    /**
     * 改变标记音乐
     */
    private GameMusic changeFlagMusic;
    /**
     * 左键点击格子音乐
     */
    private GameMusic clickBlockMusic;


    /**
     * 构造方法
     *
     * @param blocks
     * @param gameInterface
     */
    public MineListener(Block[][] blocks, GameInterface gameInterface) {
        this.blocks = blocks;
        this.gameInterface = gameInterface;
        //游戏失败音乐
        gameOverMusic = new GameMusic(ImagesData.MUSIC_MINE_BOOM);
        gameInterface.getThreadPoolExecutor().execute(gameOverMusic);
        //游戏胜利音乐
        gameWinMusic = new GameMusic(ImagesData.MUSIC_WIN);
        gameInterface.getThreadPoolExecutor().execute(gameWinMusic);
        //改变标记音乐
        changeFlagMusic = new GameMusic(ImagesData.MUSIC_CHANGE_FLAG);
        gameInterface.getThreadPoolExecutor().execute(changeFlagMusic);
        //左键点击格子音乐
        clickBlockMusic = new GameMusic(ImagesData.MUSIC_CLICK_BLOCK);
        gameInterface.getThreadPoolExecutor().execute(clickBlockMusic);
    }

    @Override
    public void mousePressed(MouseEvent e) {
        //获取格子信息
        Block block = (Block) e.getSource();
        //如果是左右键同时按下、实际效果为按住右键点击左键
        if (e.getModifiersEx() == InputEvent.BUTTON1_DOWN_MASK + InputEvent.BUTTON3_DOWN_MASK) {
            shortcut = true;
            shortcutPressed(block);
        }//如果是鼠标左键按下、没有标记且没有被点击过
        else if (e.getModifiers() == InputEvent.BUTTON1_MASK && !block.clickStatus && !block.flagStatus) {
            mousePressedAction(block);
        }//如果是鼠标右键按下且没有被点击过
        else if (e.getModifiers() == InputEvent.BUTTON3_MASK && !block.clickStatus) {
            mousePressedAction(block);
        }
    }

    /**
     * 捷径，按压时反应
     *
     * @param block
     */
    private void shortcutPressed(Block block) {
        //偏移量，方便遍历八个区域
        int[][] offset = {{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}};
        for (int[] off : offset) {
            int localRow = block.row + off[0];
            int localCol = block.col + off[1];
            //如果没有越界，则判断是否已经打开或标记，如果没有，则更换图标
            if (checkBorder(localRow, localCol)) {
                if (!blocks[localRow][localCol].clickStatus && !blocks[localRow][localCol].flagStatus) {
                    blocks[localRow][localCol].setIcon(ImagesData.ICON_BLOCK_BLANK_PRESSED);
                }
            }
        }
    }

    /**
     * 右键按下时反应
     *
     * @param block
     */
    private void mousePressedAction(Block block) {
        //更换图标，展示按下效果
        String iconName = block.getIcon().toString();
        if (iconName.equals(ImagesData.ICON_BLOCK_BLANK.toString())) {
            block.setIcon(ImagesData.ICON_BLOCK_BLANK_PRESSED);
        } else if (iconName.equals(ImagesData.ICON_BLOCK_FLAG.toString())) {
            block.setIcon(ImagesData.ICON_BLOCK_FLAG_PRESSED);
        } else if (iconName.equals(ImagesData.ICON_BLOCK_UNKNOWN.toString())) {
            block.setIcon(ImagesData.ICON_BLOCK_UNKNOWN_PRESSED);
        }
        //设置游戏状态
        gameInterface.getFunctionPanel().getGameStatus().setIcon(ImagesData.ICON_GAME_STATUS_MOUSE_PRESSED);
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        //获取格子信息
        Block block = (Block) e.getSource();
        //如果没有开始计时，那么开始计时
        if (!GameData.GAME_STATUS) {
            gameInterface.getTimer().start();
        }
        //如果时快捷键点击
        if (shortcut) {
            //重置快捷标记
            shortcut = false;
            //如果格子没被点击，恢复格子
            if (!block.clickStatus) {
                restoreBlock(block);
            }//检查是否满足展开条件
            else if (checkLocalFlagCountEqual(block)) {
                shortcutOpen(block);
            } else {
                restoreBlock(block);
            }
            //设置游戏状态
            gameInterface.getFunctionPanel().getGameStatus().setIcon(ImagesData.ICON_GAME_STATUS_NOTHING);
        }//如果是左键释放
        else if (e.getModifiers() == InputEvent.BUTTON1_MASK) {
            //如果没有标记且没有点击过
            if (!block.flagStatus && !block.clickStatus) {
                if (block.mineStatus) {
                    //如果是地雷
                    gameOver(block);
                } else {
                    //如果不是地雷
                    openBlock(block);
                }
            }
            int flag=1,unknown=2;
            //左右键同时点击的时候还要恢复标记
            if (block.flag == flag) {
                block.setIcon(ImagesData.ICON_BLOCK_FLAG);
            } else if (block.flag == unknown) {
                block.setIcon(ImagesData.ICON_BLOCK_UNKNOWN);
            }
        }//如果是鼠标右键释放且没有被点击过
        else if (e.getModifiers() == InputEvent.BUTTON3_MASK && !block.clickStatus) {
            //改变标记
            changeFlag(block);
        }
        //检查是否获胜
        try {
            checkWin();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 快速展开
     *
     * @param block
     */
    private void shortcutOpen(Block block) {
        int[][] offset = {{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}};
        //如果没有被点击过且没有被标记
        for (int[] off : offset) {
            int localRow = block.row + off[0];
            int localCol = block.col + off[1];
            //如果没有越界
            if (checkBorder(localRow, localCol)) {
                //如果是地雷并且没有标记
                if (blocks[localRow][localCol].mineStatus && !blocks[localRow][localCol].flagStatus) {
                    gameOver(blocks[localRow][localCol]);
                }//如果是未打开的格子
                else if (!blocks[localRow][localCol].clickStatus && !blocks[localRow][localCol].flagStatus) {
                    openBlock(blocks[localRow][localCol]);
                }
            }
        }
    }

    /**
     * 检查周围的地雷数目是否和自己数目相等
     *
     * @param block
     * @return
     */
    private boolean checkLocalFlagCountEqual(Block block) {
        int flagCount = 0;
        int[][] offset = {{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}};
        //如果没有被点击过且没有被标记
        for (int[] off : offset) {
            int localRow = block.row + off[0];
            int localCol = block.col + off[1];
            //如果没有越界，检查是否有标记
            if (checkBorder(localRow, localCol)) {
                if (blocks[localRow][localCol].flag == 1) {
                    flagCount++;
                }
            }
        }
        return block.localMineCount == flagCount;
    }

    /**
     * 恢复格子
     *
     * @param block
     */
    private void restoreBlock(Block block) {
        //偏移量，方便遍历八个区域
        int[][] offset = {{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}};
        for (int[] off : offset) {
            int localRow = block.row + off[0];
            int localCol = block.col + off[1];
            //如果没有越界，则判断是否已经打开或标记，如果没有，则恢复图标
            if (checkBorder(localRow, localCol)) {
                if (!blocks[localRow][localCol].clickStatus && !blocks[localRow][localCol].flagStatus) {
                    blocks[localRow][localCol].setIcon(ImagesData.ICON_BLOCK_BLANK);
                }
            }
        }
    }

    /**
     * 改变标记
     *
     * @param block
     */
    private void changeFlag(Block block) {
        changeFlagMusic.playMusic();
        switch (block.flag) {
            //用户未标记
            case 0:
                block.setIcon(ImagesData.ICON_BLOCK_FLAG);
                block.flag = 1;
                block.flagStatus = true;
                GameData.MINE_REMAINING_COUNT--;
                //地雷数目比-99更少就设置为-99
                int minCount = -99;
                if (GameData.MINE_REMAINING_COUNT < minCount) {
                    GameData.MINE_REMAINING_COUNT = minCount;
                }
                gameInterface.getFunctionPanel().setMineCount(GameData.MINE_REMAINING_COUNT);
                break;
            //用户已经标记为地雷
            case 1:
                block.setIcon(ImagesData.ICON_BLOCK_UNKNOWN);
                block.flag = 2;
                block.flagStatus = true;
                GameData.MINE_REMAINING_COUNT++;
                gameInterface.getFunctionPanel().setMineCount(GameData.MINE_REMAINING_COUNT);
                break;
            //用户已经标记为未知
            case 2:
                block.setIcon(ImagesData.ICON_BLOCK_BLANK);
                block.flag = 0;
                block.flagStatus = false;
                break;
            default:
                break;
        }
        //设置游戏状态
        gameInterface.getFunctionPanel().getGameStatus().setIcon(ImagesData.ICON_GAME_STATUS_NOTHING);
    }

    /**
     * 检查是否获胜
     *
     * @throws IOException
     */
    private void checkWin() throws IOException {
        //打开的格子数目
        int blockOpenedCount = 0;
        //期待打开的格子数目
        int blockOpenedCountExpected = GameData.ROWS * GameData.COLS - GameData.MINE_COUNT;
        //统计打开的格子数目
        for (Block[] value : blocks) {
            for (Block item : value) {
                if (item.clickStatus) {
                    blockOpenedCount++;
                }
            }
        }
        //如果打开了所有能打开的格子，用户获胜
        if (blockOpenedCount == blockOpenedCountExpected) {
            gameWinMusic.playMusic();
            //去掉鼠标监视器
            for (Block[] value : blocks) {
                for (Block item : value) {
                    item.removeMouseListener(this);
                }
            }
            //停止计时器
            gameInterface.getTimer().stop();
            //设置游戏状态为胜利
            gameInterface.getFunctionPanel().getGameStatus().setIcon(ImagesData.ICON_GAME_STATUS_WIN);
            //显示游戏胜利对话框
            showWin();
        }
    }

    /**
     * 显示游戏胜利并记录数据
     *
     * @throws IOException
     */
    private void showWin() throws IOException {
        //游戏级别判断
        String gameLevel = "";
        int level0=0,level1=1,level2=2;
        if (GameData.GAME_LEVEL == level0) {
            gameLevel = "初级扫雷胜利！请留下姓名！";
        } else if (GameData.GAME_LEVEL ==level1) {
            gameLevel = "中级扫雷胜利！请留下姓名！";
        } else if (GameData.GAME_LEVEL==level2) {
            gameLevel = "高级扫雷胜利！请留下姓名！";
        }
        //是否要更新用户记录
        if (!"".equals(gameLevel)) {
            //超过记录
            String[] rankData = checkUserScore(GameData.TIME_COUNT);
            if (rankData != null) {
                String userName = JOptionPane.showInputDialog(gameInterface, gameLevel, "游戏胜利",
                        JOptionPane.INFORMATION_MESSAGE);
                if (!"".equals(userName)) {
                    saveUserScore(userName, GameData.TIME_COUNT, rankData);
                }
            } else {//没有超过记录
                JOptionPane.showMessageDialog(gameInterface, gameLevel.split("请")[0], "游戏胜利",
                        JOptionPane.INFORMATION_MESSAGE);
            }
        } else {
            JOptionPane.showMessageDialog(gameInterface, "自定义扫雷游戏胜利！", "游戏胜利",
                    JOptionPane.INFORMATION_MESSAGE);
        }
    }

    /**
     * 保存用户信息
     *
     * @param userName
     * @param userTime
     * @param rankData
     * @throws IOException
     */
    private void saveUserScore(String userName, int userTime, String[] rankData) throws IOException {
        //游戏级别判断
        //重建文件
        File file = new File("data/rank" + GameData.GAME_LEVEL + ".txt");
        file.createNewFile();
        //写入数据
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8);
        int dataLength=10;
        for (int i = 0; i < dataLength; i++) {
            if (rankData[i] != null) {
                outputStreamWriter.write(rankData[i] + "\n");
            } else {
                outputStreamWriter.write(userName + "：" + userTime + "\n");
            }
        }
        outputStreamWriter.close();
    }

    /**
     * 检查是否保存用户信息
     *
     * @param userTime
     * @return
     * @throws IOException
     */
    private String[] checkUserScore(int userTime) throws IOException {
        //返回内容
        String[] result = new String[11];
        //检查文件是否存在
        File file = new File("data/rank" + GameData.GAME_LEVEL + ".txt");
        if (!file.exists()) {
            file.createNewFile();
        }
        //打开文件读取信息
        InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("data/rank" + GameData.GAME_LEVEL + ".txt"), StandardCharsets.UTF_8);
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
        boolean rankFlag = true;
        for (int i = 0; i < result.length - 1; i++) {
            String line = bufferedReader.readLine();
            if (line == null) {
                line = "无名：999";
            } else if ("".equals(line)) {
                line = "无名：999";
            }
            String[] lineItems = line.split("：");
            //如果比排行榜时间长
            if (Integer.parseInt(lineItems[lineItems.length - 1]) <= userTime || !rankFlag) {
                result[i] = line;
            } else {
                result[result.length - 1] = String.valueOf(i);
                result[++i] = line;
                rankFlag = false;
            }
        }
        bufferedReader.close();
        //判断返回值
        if (result[result.length - 1] == null) {
            return null;
        } else {
            return result;
        }
    }

    /**
     * 游戏失败
     *
     * @param block
     */
    private void gameOver(Block block) {
        gameOverMusic.playMusic();
        //打开全部地雷并检查用户标记
        for (Block[] value : blocks) {
            for (Block item : value) {
                //如果是地雷且用户没有标记
                if (item.mineStatus && !item.flagStatus) {
                    item.setIcon(ImagesData.ICON_BLOCK_MINE);
                }//如果不是地雷但用户标记了
                else if (!item.mineStatus && item.flagStatus) {
                    item.setIcon(ImagesData.ICON_BLOCK_MINE_ERROR);
                }
                //去掉鼠标监视器
                item.removeMouseListener(this);
            }
        }
        //单击的地雷设置爆炸效果
        block.setIcon(ImagesData.ICON_BLOCK_MINE_EXPLOSION);
        //游戏状态设置为失败
        gameInterface.getFunctionPanel().getGameStatus().setIcon(ImagesData.ICON_GAME_STATUS_FAIL);
        //停止计时器
        gameInterface.getTimer().stop();
    }

    /**
     * 打开格子
     *
     * @param block
     */
    private void openBlock(Block block) {
        clickBlockMusic.playMusic();
        //偏移量，方便遍历周围八个格子
        int[][] offset = {{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}};
        //如果没有被点击过且没有被标记
        if (!block.clickStatus && !block.flagStatus) {
            //如果是空格子
            if (block.localMineCount == 0) {
                block.setIcon(ImagesData.ICON_LOCAL_MINE_NUM[0]);
                block.clickStatus = true;
                for (int[] off : offset) {
                    int localRow = block.row + off[0];
                    int localCol = block.col + off[1];
                    //如果没有越界，打开周围的格子
                    if (checkBorder(localRow, localCol)) {
                        openBlock(blocks[localRow][localCol]);
                    }
                }
            }
            //如果不是空格子
            else {
                block.setIcon(ImagesData.ICON_LOCAL_MINE_NUM[block.localMineCount]);
                block.clickStatus = true;
            }
        }
        //设置游戏状态
        gameInterface.getFunctionPanel().getGameStatus().setIcon(ImagesData.ICON_GAME_STATUS_NOTHING);
    }

    /**
     * 检查边界
     *
     * @param row
     * @param col
     * @return
     */
    private boolean checkBorder(int row, int col) {
        return row >= 0 && row < GameData.ROWS && col >= 0 && col < GameData.COLS;
    }

    @Override
    public void mouseClicked(MouseEvent e) {

    }

    @Override
    public void mouseEntered(MouseEvent e) {

    }

    @Override
    public void mouseExited(MouseEvent e) {

    }
}
